#include "StdAfx.h"
#include "mlsscanner.h"

MLSCHAR CMLSScanner::RomanChar[] = {
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
	'0','1','2','3','4','5','6','7','8','9',0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
	0x40,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
	'P','Q','R','S','T','U','V','W','X','Y','Z',0x5B,0x5C,0x5D,0x5E,0x5F,
	0x60,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
	'p','q','r','s','t','u','v','w','x','y','z',0x7B,0x7C,0x7D,0x7E,0,
	13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,'/','|',0,0,0,0
};
MLSCHAR CMLSScanner::GreekChar[] = {
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
	'0','1','2','3','4','5','6','7','8','9',0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
	0x40,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
	'P','Q','R','S','T','U','V','W','X','Y','Z',0x5B,0x5C,0x5D,0x5E,0x5F,
	0x60,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
	'p','q','r','s','t','u','v','w','x','y','z',0x7B,0x7C,0x7D,0x7E,0,
	13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,'/','|',0,0,0,0
};
MLSCHAR CMLSScanner::ArabicChar[] = {
	0,0x81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,')','(',0,0,0,0,0,
//#ifdef _UNICODE
	0x20,0x61F,0x21,0x2013,0,0xBB,0xAB,0,0,0,0,0x686,0x60C,0x6AF,0x2E,0x627,
	0x660,0x661,0x662,0x663,0x664,0x665,0x666,0x667,0x668,0x669,0x3A,0x61B,0,0x627,0,0,
	0x698,0x627,0x628,0x62A,0x62B,0x62C,0x62D,0x62E,0x62F,0x630,0x631,0x632,0x633,0x634,0x635,0x636,
	0x637,0x638,0x639,0x63A,0x641,0x642,0x643,0x644,0x645,0x646,0x648,0x647,0x629,0x64A,0x649,0,
	0x621,0x623,0x625,0,0x622,0x626,0x624,0xF7,0x64E,0x64F,0xF2,0x64D,0x640,0x6AF,0x647,0,
	0x6A9,0,0x627,0,0,0,0,0,0,0,0,0,0,0,0,0,
	13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0x698,0,0,0,0,0,0x686,0x629,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0x64E,0x64F,0x650,0x652,
	0x64B,0x64C,0x64D,0x651,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0x670,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0x67E,0,0,0,0,
/*#else
	0x20,0xBF,0x21,0x96,0,0xBB,0xAB,0,0,0,0,0x8D,0xA1,0x90,0x2E,0xC7,
	'0','1','2','3','4','5','6','7','8','9',0x3A,0xBA,0,0xC7,0,0,
	0x8E,0xC7,0xC8,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,
	0xD8,0xD9,0xDA,0xDB,0xDD,0xDE,0xDF,0xE1,0xE3,0xE4,0xE6,0xE5,0xC9,0xED,0xEC,0,
	0xC1,0xC3,0xC5,0,0xC2,0xC6,0xC4,0xF7,0xF3,0xF5,0xF2,0xF8,0xDC,0x90,0xE5,0,
	0x98,0,0xC7,0,0,0,0,0,0,0,0,0,0,0,0,0,
	13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0x8E,0,0,0,0,0,0x8D,0xC9,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0xF3,0xF5,0xF6,0xFA,
	0xF0,0xF1,0xF2,0xF8,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0x81,0,0,0,0,
#endif*/
	0,0,0,0,0,0,0,0,0,0,'/','|',0,0,0,0
};
MLSCHAR CMLSScanner::CopticChar[] = {
	0,0x24,0x23,0,0,0,0,0,0,0,0,0,0,0,0,0x5F,
	0,0,0,0,0,0,0,0x40,0,0,0,0,0,0,0,0,
	0x20,0x72,0x59,0x74,0x75,0x76,0x79,0x79,0x28,0x29,0x2A,0x2B,0x78,0x2D,0x2E,0x68,
	0,0x61,0x62,0x67,0x64,0x65,0x2C,0x7A,0x68,0x71,0x3A,0,0,0x3D,0x58,0x48,
	0x63,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x7C,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
	0x50,0x22,0x52,0x7D,0x54,0x55,0x56,0x57,0x7B,0x51,0x5A,0x73,0,0x3B,0x2C,0,
	0x2E,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x5C,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
	0x70,0x27,0x72,0x5D,0x74,0x75,0x76,0x77,0x5B,0x71,0x7A,0x53,0,0x3A,0,0,
	13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0x60,0x60,0x60,0x60,
	0xF7,0x2F,0x3F,0xBF,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,'/','|',0,0,0,0
};

BYTE CMLSScanner::RomanAccent[] = {
	0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,
	0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE
};

BYTE CMLSScanner::GreekAccent[] = {
	0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,
	0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE
};

BYTE CMLSScanner::ArabicAccent[] = {
	0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,
	0xC9,0xC8,0,0,0,0,0,0,0,0
};

BYTE CMLSScanner::CopticAccent[] = {
	0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,
	0,0,0,0,0,0,0,0,0,0
};

// Constructor & Destructor ---------------------------------------------------------
CMLSScanner::CMLSScanner(void):Roman(0),Arabic(1),Coptic(2),Greek(3)
	,CurrentParagraph(0),CurrentCharacter(0),CurrentGlobalCharacter(0)
{}

CMLSScanner::~CMLSScanner(void)
{
	Close();
}


// File Management ------------------------------------------------------------------
BOOL CMLSScanner::Open(CString FileName)
{
	char Header[0x28];
	char Prev[13];Prev[12]=0;
	char Next[13];Next[12]=0;
	if(CBinaryDS::Open(FileName))
	{
		if(CBinaryDS::ReadBlock((PBYTE)Header, 0x28)==0x28&&CBinaryDS::ReadBlock((PBYTE)Prev, 12)==12&&CBinaryDS::ReadBlock((PBYTE)Next, 12)==12)
		Header[3]=0;
		if(CString(Header)=="MLS")
		{
			Header[8]=0;int n;
			for(n=0;n<12;n++) if(Prev[n]==0x20) {Prev[n]=0;break;}
			for(n=0;n<12;n++) if(Next[n]==0x20) {Next[n]=0;break;}
			Version = Header + 4;
			PrevDoc = Prev;
			NextDoc = Next;
			for(n=0;n<5;n++) bIsLangSupported[n]=FALSE;
			for(n=0x16;n<0x26;n+=3)
				if(Header[n]>=0x30&&Header[n]<0x36) bIsLangSupported[Header[n]-0x30]=TRUE;
			if(CFile::Seek(0x190,CFile::begin)==0x190) return TRUE;
		}
	}
	CBinaryDS::Close();
	return FALSE;
}

void CMLSScanner::Close(void)
{
	CBinaryDS::Close();
}

// Stack Popers : Public ------------------------------------------------------------
CMLSScanner::MLSToken CMLSScanner::GetToken(void)
{
	//CMLSScanner::MLSToken tk;
	FillStack();
	return PopTarget();
}
// Stack Popers : Protected ---------------------------------------------------------
CMLSScanner::MLSToken CMLSScanner::PopTarget()
{
	BYTE Raw=0;
	Language = StackLang;
	for(TargetCharacter=0;TargetCharacter==0&&!Stack.IsEmpty();)
	{
		if(Language==Roman)
			TargetCharacter = RomanChar[Raw = Stack.Pop()];
		else if(Language==Arabic)
			TargetCharacter = ArabicChar[Raw = Stack.Pop()];
		else if(Language==Coptic)
			TargetCharacter = CopticChar[Raw = Stack.Pop()];
		else if(Language==Greek)
			TargetCharacter = GreekChar[Raw = Stack.Pop()];
		else//Assume Roman
			TargetCharacter = RomanChar[Raw = Stack.Pop()];
	}
	CurrentCharacter++;
	CurrentGlobalCharacter++;
	if(Raw==0x80){CurrentParagraph++;CurrentCharacter=0;}
	return ResolveToken(Raw,Language);
}
// Stack Filler : Protected ---------------------------------------------------------
CMLSScanner::MLSToken CMLSScanner::FillStack()
{
	if(Stack.IsEmpty())
	{
		if(CBinaryDS::IsEOF())
		{
			PushChar(0xFF, StackLang);
			return mlstk_EndOfFile;
		}
		else
		{
			do
			{
				RawCharacter = Get();
				BYTE Descriptor = Get();
				StackLang = Descriptor & 0x0F;
				Flags = (Descriptor & 0xF0) >> 4;
				if(Flags & mlsfl_Accented)
				{
					Accents = Get()+(Get()<<8);
					PushPostAccent(Accents, StackLang);
					PushChar(RawCharacter, StackLang);
					PushPreAccent(Accents, StackLang);
				}
				else
				{
					Accents = 0;
					PushChar(RawCharacter, StackLang);
				}
				if(StackLang!=Arabic||RawCharacter<0x30||
					RawCharacter>0x39) break;
			}
			while((CBinaryDS::Look(1)&0x0F)==1&&
				CBinaryDS::Look()>=0x30&&CBinaryDS::Look()<=0x39);
		}
	}
	return ResolveToken(Stack.GetHead(), StackLang);
}

void CMLSScanner::PushChar(BYTE Raw, int Lang) // Fill: TargetString
{
	static BYTE LastRaw = 0;
	static int LastLang = 0;

	if(Lang==Arabic)
	{
		if(Raw>=0x67&&Raw<=0x6A)
		{
			Raw-=7;if(Raw==0x63)Raw++;else if(Raw==0x60)Raw=0x41;
			Stack.Push(Raw);
			Raw=0x57;
		}
		else if(Raw==0xB4||Raw==0xB5)
		{
			Stack.Push(Raw-8);
			Raw=0xB3;
		}
		else if(Raw==0x6C&&LastRaw==0x20)
		{
			Raw=0x23;
		}
	}
	else if(Lang==Coptic)
	{
		if((Raw>0x20&&Raw<0x27&&Raw!=0x22)||
			(Raw>0x30&&Raw<0x3A)||Raw==0x40)
			Stack.Push(0xB1);
	}
	Stack.Push(Raw);

	LastRaw = Raw;
	LastLang = Lang;
}
void CMLSScanner::PushPreAccent(WORD AccentsPattern, int Lang)
{
	if(Lang!=Coptic) return;// Should be removed in future
	for(int n=0;n<3;n++)
	{
		int x = (AccentsPattern>>(n*5))&0x1F;
		if(x)
		{
		//if(Lang==Coptic)
		//{
			if(x<5) PushChar(CopticAccent[x-1],Lang);
		//}
		}
	}
}
void CMLSScanner::PushPostAccent(WORD AccentsPattern, int Lang)
{
	for(int n=0;n<3;n++)
	{
		int x = (AccentsPattern>>(n*5))&0x1F;
		if(x)
		{
			if(Lang==Roman)
			{
				if(RomanAccent[x-1]) PushChar(RomanAccent[x-1],Lang);
			}
			else if(Lang==Arabic)
			{
				if(ArabicAccent[x-1]) PushChar(ArabicAccent[x-1],Lang);
			}
			else if(Lang==Coptic)
			{
				if(CopticAccent[x-1]&&x>4) PushChar(CopticAccent[x-1],Lang);
			}
			else if(Lang==Greek)
			{
				if(GreekAccent[x-1]&&x>4) PushChar(GreekAccent[x-1],Lang);
			}
		}
	}
}

// Resolvers : Aheaders -------------------------------------------------------------
CMLSScanner::MLSToken CMLSScanner::GetTokenAhead(void)
{
	return FillStack();
}
CMLSScanner::MLSToken CMLSScanner::GetSecToken(void)
{
	if(FillStack()==mlstk_EndOfFile)return mlstk_EndOfFile;
	return ResolveToken(CBinaryDS::Look(),CBinaryDS::Look(1));
}

BYTE CMLSScanner::GetRawAhead()
{
	FillStack();
	return Stack.GetHead();
}

int CMLSScanner::GetLangAhead()
{
	FillStack();
	return StackLang;
}

// Static Resolver For a Token ------------------------------------------------------
CMLSScanner::MLSToken CMLSScanner::ResolveToken(BYTE Raw, int Lang)
{
	if(Raw ==0) return mlstk_NoToken;
	else if(Raw == 0xFF)
		return mlstk_EndOfFile;
	else if(Raw == mlsch_Semicolon && Lang == Roman)
		return mlstk_Semicolon;
	else if(Raw == mlsch_NewLine)
		return mlstk_NewLine;
	else if(Raw == mlsch_Delimiter)
		return mlstk_Delimiter;
	else if(Raw == mlsch_Embed)
		return mlstk_Embed;
	else if(Raw == mlsch_HardSpace)
		return mlstk_HardSpace;
	else if(Raw == mlsch_ReverseIndent)
		return mlstk_ReverseIndent;
	else if(Raw == mlsch_ZeroSpace)
		return mlstk_ZeroSpace;
	else
		return mlstk_Character;
}
